// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.

#include "StdAfx.h"
#include "QvXmlDocument.h"
#include "QvXmlNodeList.h"
#include "QvXmlNode.h"
#include "QvXmlElement.h"
#include "QvDebug.h"

QvXmlDocument::QvXmlDocument() : QvObject(),
    m_pChildNodes(NULL),
    m_pXMLDoc(NULL),
    m_strXml(NULL)
{
    HRESULT hr = S_OK;

    hr = CoCreateInstance(__uuidof(DOMDocument30),
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          IID_PPV_ARGS(&m_pXMLDoc));

    if (FAILED(hr))
    {
        QvDebug::PrintDebugString(L"Create IXMLDOMDocument Fail!");
    }
}

QvXmlDocument::QvXmlDocument(QvXmlDocument const & obj) : m_strXml(NULL)
{
    m_pXMLDoc = obj.m_pXMLDoc;
    if(m_pXMLDoc)
    {
        m_pXMLDoc->AddRef();
    }

    m_pChildNodes = obj.m_pChildNodes;
    if(m_pChildNodes)
    {
        m_pChildNodes->AddRef();
    }
}

QvXmlDocument& QvXmlDocument::operator=(QvXmlDocument const & obj)
{
    m_strXml = NULL;

    if(m_pChildNodes)
    {
        m_pChildNodes->Release();
    }
    m_pChildNodes = obj.m_pChildNodes;
    if(m_pChildNodes)
    {
        m_pChildNodes->AddRef();
    }

    if(m_pXMLDoc)
    {
        m_pXMLDoc->Release();
    }
    m_pXMLDoc = obj.m_pXMLDoc;
    if(m_pXMLDoc)
    {
        m_pXMLDoc->AddRef();
    }
    return *this;
}


QvXmlDocument::~QvXmlDocument()
{
    if(NULL != m_pChildNodes)
    {
        m_pChildNodes->Release();
        m_pChildNodes = NULL;
    }
    if (NULL != m_pXMLDoc)
    {
        int nCount = m_pXMLDoc->Release();
        m_pXMLDoc = NULL;
    }
    if(m_strXml)
    {
        ::SysFreeString(m_strXml);
    }
}

QvBoolean QvXmlDocument::SaveToFile(const QvChar* wcsFilePath)
{
    if ((NULL != wcsFilePath) && (NULL != m_pXMLDoc))
    {
        VARIANT varFile;
        //
        // Initialize the VARIANT with the filename
        //
        ::VariantInit(&varFile);
        varFile.bstrVal = ::SysAllocString(wcsFilePath);
        varFile.vt = VT_BSTR;

        HRESULT hr = m_pXMLDoc->save(varFile);

        //
        // Cleanup the VARIANT; this will free the allocated BSTR
        //
        ::VariantClear(&varFile);
        if (SUCCEEDED(hr))
        {
            return TRUE;
        }
    }
    return FALSE;
}

QvBoolean QvXmlDocument::LoadFromFile(const QvChar* wcsFilePath)
{
    VARIANT_BOOL isLoaded = VARIANT_FALSE;
    if(NULL != m_pChildNodes)
    {
        SAFE_DELETE(m_pChildNodes);
        m_pChildNodes = NULL;
    }
    if ((NULL != wcsFilePath) && (NULL != m_pXMLDoc))
    {
        HRESULT hr = S_OK;
        VARIANT varFile;
        //
        // Initialize the VARIANT with the filename
        //
        ::VariantInit(&varFile);
        varFile.bstrVal = ::SysAllocString(wcsFilePath);
        varFile.vt = VT_BSTR;

        hr = m_pXMLDoc->load(varFile, &isLoaded);

        if (FAILED(hr) || (isLoaded != VARIANT_TRUE))
        {
            QvDebug::PrintDebugString(L"Failed to load xml document, hr = %d", hr);
        }
        //
        // Cleanup the VARIANT; this will free the allocated BSTR
        //
        ::VariantClear(&varFile);
    }

    return (VARIANT_TRUE == isLoaded);
}

QvXmlNodeList* QvXmlDocument::GetChildNodes()
{
    //
    // If there is a list of child nodes, return it.
    // Otherwise, generate it from the IXMLDOMDocument
    //
    if ((NULL == m_pChildNodes) && (NULL != m_pXMLDoc))
    {
        HRESULT         hr = S_OK;
        IXMLDOMNodeList *pChildNodes = NULL;
        IXMLDOMElement  *pDocElement = NULL;

        hr = m_pXMLDoc->get_documentElement(&pDocElement);
        if (SUCCEEDED(hr) && (NULL != pDocElement))
        {
            hr = pDocElement->get_childNodes(&pChildNodes);
            if (SUCCEEDED(hr) && (NULL != pChildNodes))
            {
                m_pChildNodes = new QvXmlNodeList(pChildNodes);
                //
                // Release the child nodes; QvXmlNodeList will
                // retain a reference.
                //
                pChildNodes->Release();
                pChildNodes = NULL;
            }
            //
            // Release the element
            //
            pDocElement->Release();
            pDocElement = NULL;
        }
    }
    else if (NULL != m_pChildNodes)
    {
        m_pChildNodes->AddRef();
    }
    return m_pChildNodes;
}


QvXmlElement* QvXmlDocument::AddElement(const QvChar* pwszName)
{
    HRESULT         hr = S_OK;
    QvXmlElement    *pElement = NULL;
    IXMLDOMElement  *pIElement = NULL;
    IXMLDOMNode     *pNewNode = NULL;
    BSTR            bstrName = ::SysAllocString(pwszName);

    if (NULL != m_pXMLDoc)
    {
        hr = m_pXMLDoc->createElement(bstrName, &pIElement);
        if (SUCCEEDED(hr))
        {
            hr = m_pXMLDoc->appendChild(pIElement, &pNewNode);
            if (SUCCEEDED(hr))
            {
                pElement = new QvXmlElement(m_pXMLDoc, pIElement);
                //
                // Release the new node
                //
                pNewNode->Release();
                pNewNode = NULL;
            }
            //
            // Release the Element
            //
            pIElement->Release();
            pIElement = NULL;
        }
    }
    ::SysFreeString(bstrName);

    return pElement;
}

const QvChar* QvXmlDocument::GetXml()
{
    ::SysFreeString(m_strXml);
    if(m_pXMLDoc)
    {
        HRESULT hr = m_pXMLDoc->get_xml(&m_strXml);
        if (FAILED(hr))
        {
            ::SysFreeString(m_strXml);
        }
    }
    return m_strXml;
}
